package org.example.leetCode;

/**
 * 416. 分割等和子集
 * 给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集，使得两个子集的元素和相等。
 */
public class Code416 {
    public boolean canPartition(int[] nums) {
        int n = nums.length, sum = 0;
        // 计算数组总和
        for (int num : nums) {
            sum += num;
        }
        // 如果总和为奇数，无法分割成两个相等的子集
        if (sum % 2 == 1) {
            return false;
        }
        int target = sum / 2;

        // dp[i][j] 表示前i个数字中是否存在和为j的子集
        boolean[][] dp = new boolean[n + 1][target + 1];

        // 初始化：前0个数字组成和为0的子集总是存在（空集）
        for (int i = 0; i <= n; i++) {
            dp[i][0] = true;
        }

        // 遍历物品
        for (int i = 1; i <= n; i++) {
            // 遍历背包容量
            for (int j = 1; j <= target; j++) {
                // 不选择第i个数字
                dp[i][j] = dp[i - 1][j];
                // 如果当前数字不超过背包容量，可以考虑选择它
                if (j >= nums[i - 1]) {
                    // 选择第i个数字 或 不选择第i个数字
                    dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i - 1]];
                }
            }
        }

        // 返回前n个数字中是否存在和为target的子集
        return dp[n][target];
    }
    public boolean canPartition1(int[] nums) {
        int n = nums.length, sum = 0;
        // 计算数组总和
        for (int num : nums) {
            sum += num;
        }
        // 如果总和为奇数，无法分割成两个相等的子集
        if (sum % 2 == 1) {
            return false;
        }
        int target = sum / 2;

        // dp[j] 表示容量为j的背包最多能装的物品价值
        int[] dp = new int[target + 1];

        // 遍历物品
        for (int i = 0; i < n; i++) {
            // 遍历背包容量（从大到小，避免重复使用物品）
            for (int j = target; j >= nums[i]; j--) {
                // 状态转移方程：不放物品i vs 放物品i
                dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i]);
            }
        }
        // 如果能装满容量为target的背包，说明可以分割
        return dp[target] == target;
    }
    public static void main(String[] args) {
        Code416 solution = new Code416();

        // 测试用例1: [1,5,11,5] -> true
        int[] nums1 = {1, 5, 11, 5};
        System.out.println("数组 [1,5,11,5] 是否可以分割成两个等和子集: " + solution.canPartition(nums1));

        // 测试用例2: [1,2,3,5] -> false
        int[] nums2 = {1, 2, 3, 5};
        System.out.println("数组 [1,2,3,5] 是否可以分割成两个等和子集: " + solution.canPartition(nums2));

        // 测试用例3: [1,2,5] -> false
        int[] nums3 = {1, 2, 5};
        System.out.println("数组 [1,2,5] 是否可以分割成两个等和子集: " + solution.canPartition(nums3));

        // 测试用例4: [1,1] -> true
        int[] nums4 = {1, 1};
        System.out.println("数组 [1,1] 是否可以分割成两个等和子集: " + solution.canPartition(nums4));
    }
}
